package com.wngbms.fileio.sharemem;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

public class ShareMemory {

    int fsize;
    RandomAccessFile randomAccessFile;
    FileChannel fileChannel;
    MappedByteBuffer mappedByteBuffer;

    public ShareMemory() {
        try {
            fsize = 1024;
            randomAccessFile = new RandomAccessFile("123.txt", "rw");
            fileChannel = randomAccessFile.getChannel();
            mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, fsize);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        ShareMemory shareMemory = new ShareMemory();

//        shareMemory.fileChannel.lock();
//        shareMemory.fileChannel.lock(); // java.nio.channels.OverlappingFileLockException
//        shareMemory.fileChannel.lock();

//        shareMemory.fileChannel.lock();
//        new Thread(() -> {
//            try {
//                shareMemory.fileChannel.lock();
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//        }).start();
//        sleep(10000);

//        shareMemory.fileChannel.lock(0, 10, false);
//        shareMemory.fileChannel.lock(10, 15, false); // ok

//        shareMemory.fileChannel.lock(0, 10, false);
//        shareMemory.fileChannel.lock(0, 10, true); // OverlappingFileLockException

//        shareMemory.fileChannel.lock(0, 10, true);
//        shareMemory.fileChannel.lock(0, 10, false); // OverlappingFileLockException

//        shareMemory.fileChannel.lock(0, 10, true);
//        shareMemory.fileChannel.lock(0, 10, true); // OverlappingFileLockException

//        shareMemory.fileChannel.close();
//        shareMemory.fileChannel.lock(0, 10, true); // ClosedChannelException

//        new Thread(() -> {
//            try {
//                shareMemory.fileChannel.close();
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//        }).start();
//        sleep(1000);
//        shareMemory.fileChannel.lock(0, 10, true);

//        FileLock lock = shareMemory.fileChannel.lock(0, 10, true);  // 开多进程 而不是多线程, 不会阻塞
//        System.out.println(lock.isShared());
//        sleep(30000);

//        FileLock lock = shareMemory.fileChannel.lock(0, 10, false);  // 开多进程 而不是多线程, 会阻塞
//        System.out.println(lock.isShared());
//        sleep(30000);

//        FileLock lock = shareMemory.fileChannel.lock(0, 10, false);  // 开多进程 而不是多线程, ....
//        System.out.println(lock.isShared());
//        sleep(10000);
//        shareMemory.fileChannel.close();
//        sleep(30000);

//        FileLock lock = shareMemory.fileChannel.lock(0, 10, false);  // 开多进程 而不是多线程, 区域不同不会阻塞
//        FileLock lock1 = shareMemory.fileChannel.lock(10, 20, false);  // 开多进程 而不是多线程, 区域不同不会阻塞
//        System.out.println("get lock");
//        sleep(30000);

//        FileLock lock = shareMemory.fileChannel.lock(0, 10, false);  // 开多进程 而不是多线程, 有重叠区域会阻塞
//        FileLock lock1 = shareMemory.fileChannel.lock(5, 20, false);  // 开多进程 而不是多线程, 有重叠区域会阻塞
//        System.out.println("get lock");
//        sleep(30000);

//        shareMemory.fileChannel.lock(0, 10, true);
//        System.out.println("get lock");
//        sleep(30000);
    }

    public int write(int pos, int len, byte[] buff) {
        if (pos >= fsize || pos + len >= fsize) {
            return -1;
        }

        FileLock fileLock = null;
        try {
            // 同一进程多线程同时调用此方法, 如果有重叠区域 则会抛异常
            // 不同进程调用同一文件的锁方法, 如果有重叠区域, 则会阻塞线程
            fileLock = fileChannel.lock(pos, len, false);

            mappedByteBuffer.position(pos);
            mappedByteBuffer.put(ByteBuffer.wrap(buff));

            return len;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fileLock != null) {
                try {
                    fileLock.release();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return -1;
    }

    public int read(int pos, int len, byte[] buff) {
        if (pos >= fsize) {
            return -1;
        }

        FileLock fileLock = null;
        try {
            fileLock = fileChannel.lock(pos, len, true);
            mappedByteBuffer.position(pos);

            if (mappedByteBuffer.remaining() < len) {
                len = mappedByteBuffer.remaining();
            }

            if (len > 0) {
                mappedByteBuffer.get(buff, 0, len);
            }

            return len;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileLock != null) {
                try {
                    fileLock.release();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return -1;
    }

    public void close() {
        if (fileChannel != null) {
            try {
                fileChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (randomAccessFile != null) {
            try {
                randomAccessFile.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void sleep(long t) {
        try {
            Thread.sleep(t);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
